#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/of.h>

#define ST7789_DEV_NAME "st7789"
#define ST7789_WIDTH  240
#define ST7789_HEIGHT 240

// ST7789 命令定义
#define ST7789_NOP     0x00
#define ST7789_SWRESET 0x01
#define ST7789_SLPOUT  0x11
#define ST7789_DISPON  0x29
#define ST7789_CASET   0x2A
#define ST7789_RASET   0x2B
#define ST7789_RAMWR   0x2C
#define ST7789_MADCTL  0x36

// IOCTL 命令
#define ST7789_IOCTL_CLEAR_SCREEN _IO('S', 0)
#define ST7789_IOCTL_SET_PIXEL    _IOW('S', 1, unsigned long)
#define ST7789_IOCTL_FILL_RECT    _IOW('S', 2, unsigned long)

struct st7789_dev {
    struct spi_device *spi;
    struct cdev cdev;
    dev_t devno;
    struct class *class;
    struct gpio_desc *dc;    // 数据/命令引脚
    struct gpio_desc *rst;   // 复位引脚
    struct gpio_desc *bl;    // 背光引脚
};

// 向ST7789写入命令
static void st7789_write_cmd(struct st7789_dev *dev, u8 cmd)
{
    gpiod_set_value(dev->dc, 0);  // 命令模式
    spi_write(dev->spi, &cmd, 1);
}

// 向ST7789写入数据
static void st7789_write_data(struct st7789_dev *dev, const u8 *data, size_t len)
{
    gpiod_set_value(dev->dc, 1);  // 数据模式
    spi_write(dev->spi, data, len);
}

// 向ST7789写入数据
static void st7789_write_byte(struct st7789_dev *dev, const u8 data)
{
    u8 byte = data;
    gpiod_set_value(dev->dc, 1);  // 数据模式
    spi_write(dev->spi, &byte, 1);
}


// 初始化ST7789
static void st7789_init(struct st7789_dev *dev)
{
        // 复位显示屏
    if (dev->rst) {
        gpiod_set_value(dev->rst, 0);
        msleep(10);
        gpiod_set_value(dev->rst, 1);
        msleep(120);
    }

    // 软件复位
    st7789_write_cmd(dev, ST7789_SWRESET);
    msleep(150);
//************* Start Initial Sequence **********//
	st7789_write_cmd(dev,0x11); //Sleep out 
	msleep(120);              //Delay 120ms 
	//************* Start Initial Sequence **********// 
	st7789_write_cmd(dev,0x36);

	st7789_write_byte(dev,0x00);

	st7789_write_cmd(dev,0x3A);     
	st7789_write_byte(dev,0x05);   

	st7789_write_cmd(dev,0xB2);     
	st7789_write_byte(dev,0x1F);   
	st7789_write_byte(dev,0x1F);   
	st7789_write_byte(dev,0x00);   
	st7789_write_byte(dev,0x33);   
	st7789_write_byte(dev,0x33);   

	st7789_write_cmd(dev,0xB7);     
	st7789_write_byte(dev,0x35);   

	st7789_write_cmd(dev,0xBB);     
	st7789_write_byte(dev,0x2B);   //2b

	st7789_write_cmd(dev,0xC0);     
	st7789_write_byte(dev,0x2C);   

	st7789_write_cmd(dev,0xC2);     
	st7789_write_byte(dev,0x01);   

	st7789_write_cmd(dev,0xC3);     
	st7789_write_byte(dev,0x0F);   

	st7789_write_cmd(dev,0xC4);     
	st7789_write_byte(dev,0x20);   //VDV, 0x20:0v

	st7789_write_cmd(dev,0xC6);     
	st7789_write_byte(dev,0x13);   //0x13:60Hz   

	st7789_write_cmd(dev,0xD0);     
	st7789_write_byte(dev,0xA4);   
	st7789_write_byte(dev,0xA1);   

	st7789_write_cmd(dev,0xD6);     
	st7789_write_byte(dev,0xA1);   //sleep in后，gate输出为GND

	st7789_write_cmd(dev,0xE0);     
	st7789_write_byte(dev,0xF0);   
	st7789_write_byte(dev,0x04);   
	st7789_write_byte(dev,0x07);   
	st7789_write_byte(dev,0x04);   
	st7789_write_byte(dev,0x04);   
	st7789_write_byte(dev,0x04);   
	st7789_write_byte(dev,0x25);   
	st7789_write_byte(dev,0x33);   
	st7789_write_byte(dev,0x3C);   
	st7789_write_byte(dev,0x36);   
	st7789_write_byte(dev,0x14);   
	st7789_write_byte(dev,0x12);   
	st7789_write_byte(dev,0x29);   
	st7789_write_byte(dev,0x30);   

	st7789_write_cmd(dev,0xE1);     
	st7789_write_byte(dev,0xF0);   
	st7789_write_byte(dev,0x02);   
	st7789_write_byte(dev,0x04);   
	st7789_write_byte(dev,0x05);   
	st7789_write_byte(dev,0x05);   
	st7789_write_byte(dev,0x21);   
	st7789_write_byte(dev,0x25);   
	st7789_write_byte(dev,0x32);   
	st7789_write_byte(dev,0x3B);   
	st7789_write_byte(dev,0x38);   
	st7789_write_byte(dev,0x12);   
	st7789_write_byte(dev,0x14);   
	st7789_write_byte(dev,0x27);   
	st7789_write_byte(dev,0x31);   

	st7789_write_cmd(dev,0xE4);     
	st7789_write_byte(dev,0x1D);   //使用240根gate  (N+1)*8
	st7789_write_byte(dev,0x00);   //设定gate起点位置
	st7789_write_byte(dev,0x00);   //当gate没有用完时，bit4(TMG)设为0

	st7789_write_cmd(dev,0x21);     

	st7789_write_cmd(dev,0x29); 
}

// 设置显示窗口
static void st7789_set_window(struct st7789_dev *dev, u16 x0, u16 y0, u16 x1, u16 y1)
{
    u8 data[4];

    // 设置列地址
    st7789_write_cmd(dev, ST7789_CASET);
    data[0] = (x0 >> 8) & 0xFF;
    data[1] = x0 & 0xFF;
    data[2] = (x1 >> 8) & 0xFF;
    data[3] = x1 & 0xFF;
    st7789_write_data(dev, data, 4);

    // 设置行地址
    st7789_write_cmd(dev, ST7789_RASET);
    data[0] = (y0 >> 8) & 0xFF;
    data[1] = y0 & 0xFF;
    data[2] = (y1 >> 8) & 0xFF;
    data[3] = y1 & 0xFF;
    st7789_write_data(dev, data, 4);

    // 准备写入GRAM
    st7789_write_cmd(dev, ST7789_RAMWR);
}

// 清屏函数
static void st7789_clear_screen(struct st7789_dev *dev, u16 color)
{
    int x, y;
    u8 data[2];
    
    // 转换为RGB565格式
    data[0] = color >> 8;
    data[1] = color & 0xFF;
    
    // 设置全屏窗口
    st7789_set_window(dev, 0, 0, ST7789_WIDTH - 1, ST7789_HEIGHT - 1);
    
    // 填充颜色
    gpiod_set_value(dev->dc, 1);  // 数据模式
    for (y = 0; y < ST7789_HEIGHT; y++) {
        for (x = 0; x < ST7789_WIDTH; x++) {
            spi_write(dev->spi, data, 2);
        }
    }
}

// 设置单个像素
static void st7789_set_pixel(struct st7789_dev *dev, u16 x, u16 y, u16 color)
{
    if (x >= ST7789_WIDTH || y >= ST7789_HEIGHT)
        return;
    
    u8 data[2];
    data[0] = color >> 8;
    data[1] = color & 0xFF;
    
    st7789_set_window(dev, x, y, x, y);
    st7789_write_data(dev, data, 2);
}

// 填充矩形
static void st7789_fill_rect(struct st7789_dev *dev, u16 x0, u16 y0, u16 x1, u16 y1, u16 color)
{
    int x, y;
    u8 data[2];
    
    if (x0 >= ST7789_WIDTH || y0 >= ST7789_HEIGHT ||
        x1 >= ST7789_WIDTH || y1 >= ST7789_HEIGHT || x0 > x1 || y0 > y1)
        return;
    
    data[0] = color >> 8;
    data[1] = color & 0xFF;
    
    st7789_set_window(dev, x0, y0, x1, y1);
    gpiod_set_value(dev->dc, 1);  // 数据模式
    
    for (y = y0; y <= y1; y++) {
        for (x = x0; x <= x1; x++) {
            spi_write(dev->spi, data, 2);
        }
    }
}

// 文件操作 - 打开
static int st7789_open(struct inode *inode, struct file *file)
{
    struct st7789_dev *dev = container_of(inode->i_cdev, struct st7789_dev, cdev);
    file->private_data = dev;
    return 0;
}

// 文件操作 - IOCTL
static long st7789_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct st7789_dev *dev = file->private_data;
    u32 tmp[5];  // 用于传递参数: x0, y0, x1, y1, color
    
    switch (cmd) {
        case ST7789_IOCTL_CLEAR_SCREEN:
            if (copy_from_user(&tmp[0], (void __user *)arg, sizeof(u32)))
                return -EFAULT;
            st7789_clear_screen(dev, tmp[0]);
            break;
            
        case ST7789_IOCTL_SET_PIXEL:
            if (copy_from_user(tmp, (void __user *)arg, 3 * sizeof(u32)))
                return -EFAULT;
            st7789_set_pixel(dev, tmp[0], tmp[1], tmp[2]);
            break;
            
        case ST7789_IOCTL_FILL_RECT:
            if (copy_from_user(tmp, (void __user *)arg, 5 * sizeof(u32)))
                return -EFAULT;
            st7789_fill_rect(dev, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]);
            break;
            
        default:
            return -ENOTTY;
    }
    return 0;
}

// 文件操作集合
static const struct file_operations st7789_fops = {
    .owner = THIS_MODULE,
    .open = st7789_open,
    .unlocked_ioctl = st7789_ioctl,
};

// 驱动探测函数
static int st7789_probe(struct spi_device *spi)
{
    struct st7789_dev *dev;
    int ret;
    
    // 分配设备结构
    dev = devm_kzalloc(&spi->dev, sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;
    
    dev->spi = spi;
    spi_set_drvdata(spi, dev);
    
    // 获取GPIO引脚
    dev->dc = devm_gpiod_get(&spi->dev, "dc", GPIOD_OUT_LOW);
    if (IS_ERR(dev->dc)) {
        dev_err(&spi->dev, "无法获取DC GPIO: %ld\n", PTR_ERR(dev->dc));
        return PTR_ERR(dev->dc);
    }
    
    dev->rst = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH);
    if (IS_ERR(dev->rst)) {
        dev_err(&spi->dev, "无法获取复位GPIO: %ld\n", PTR_ERR(dev->rst));
        return PTR_ERR(dev->rst);
    }
    
    
    // 配置SPI总线参数
    spi->mode = SPI_MODE_3;       
    spi->bits_per_word = 8;       
    spi->max_speed_hz = 40000000; 
    
    // 应用SPI配置
    ret = spi_setup(spi);
    if (ret < 0) {
        dev_err(&spi->dev, "SPI设置失败: %d\n", ret);
        return ret;
    }

    // 申请设备号
    ret = alloc_chrdev_region(&dev->devno, 0, 1, ST7789_DEV_NAME);
    if (ret < 0) {
        dev_err(&spi->dev, "无法分配设备号\n");
        return ret;
    }
    
    // 初始化字符设备
    cdev_init(&dev->cdev, &st7789_fops);
    dev->cdev.owner = THIS_MODULE;
    ret = cdev_add(&dev->cdev, dev->devno, 1);
    if (ret < 0) {
        dev_err(&spi->dev, "无法添加字符设备\n");
        unregister_chrdev_region(dev->devno, 1);
        return ret;
    }
    
    // 创建设备类
    dev->class = class_create(THIS_MODULE, ST7789_DEV_NAME);
    if (IS_ERR(dev->class)) {
        dev_err(&spi->dev, "无法创建设备类\n");
        cdev_del(&dev->cdev);
        unregister_chrdev_region(dev->devno, 1);
        return PTR_ERR(dev->class);
    }
    
    // 创建设备节点
    if (device_create(dev->class, &spi->dev, dev->devno, NULL, ST7789_DEV_NAME) == NULL) {
        dev_err(&spi->dev, "无法创建设备节点\n");
        class_destroy(dev->class);
        cdev_del(&dev->cdev);
        unregister_chrdev_region(dev->devno, 1);
        return -EFAULT;
    }
    
    // 初始化显示屏
    st7789_init(dev);
    st7789_clear_screen(dev, 0xFFFF);  // 清屏为黑色
    
    dev_info(&spi->dev, "ST7789驱动初始化成功\n");
    return 0;
}

// 驱动移除函数
static void st7789_remove(struct spi_device *spi)
{
    struct st7789_dev *dev = spi_get_drvdata(spi);
    
    // 清理资源
    device_destroy(dev->class, dev->devno);
    class_destroy(dev->class);
    cdev_del(&dev->cdev);
    unregister_chrdev_region(dev->devno, 1);
    
    return;
}

// 设备树匹配表
static const struct of_device_id st7789_of_match[] = {
    { .compatible = "sitronix,st7789" },
    { /* 终止符 */ }
};
MODULE_DEVICE_TABLE(of, st7789_of_match);

// SPI驱动结构体
static struct spi_driver st7789_driver = {
    .driver = {
        .name = "st7789",
        .of_match_table = st7789_of_match,
    },
    .probe = st7789_probe,
    .remove = st7789_remove,
};
module_spi_driver(st7789_driver);

MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("ST7789 SPI显示屏直接驱动(不依赖framebuffer)");
MODULE_LICENSE("GPL");
    